//
//  ZYHTTPRequestSerializer.m
//  ZYWebImage
//
//  Created by wangzhipan on 2025/4/2.
//

#import "ZYHTTPRequestSerializer.h"
#import "ZYParamsParse.h"
#import "ZYMultipartFormDataStream.h"



@interface ZYHTTPRequestSerializer()

@property (nonatomic, assign) NSStringEncoding stringEncoding;
@property (readwrite, nonatomic, strong) NSMutableDictionary <NSString *, NSString *> *mutableHTTPRequestHeaders;
@property (nonatomic, strong, nullable) dispatch_queue_t requestHeaderModificationQueue;
@end

@implementation ZYHTTPRequestSerializer
+ (instancetype)serializer
{
    return [[self alloc] init];
}

- (instancetype)init
{
    if (self = [super init]) {
        self.stringEncoding = NSUTF8StringEncoding;
        self.timeoutInterval = 20;
        self.mutableHTTPRequestHeaders = [NSMutableDictionary new];
        self.requestHeaderModificationQueue = dispatch_queue_create("requestHeaderModificationQueue", DISPATCH_QUEUE_SERIAL);
        
        NSMutableArray *acceptLanguagesComponents = [NSMutableArray array];
        [[NSLocale preferredLanguages] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
            float q = 1.0f - (idx * 0.1f);
            [acceptLanguagesComponents addObject:[NSString stringWithFormat:@"%@;q=%0.1g", obj, q]];
            *stop = q <= 0.5f;
        }];
        [self setValue:[acceptLanguagesComponents componentsJoinedByString:@", "] forHTTPHeaderField:@"Accept-Language"];
        NSString *userAgent = [NSString stringWithFormat:@"%@/%@ (%@; iOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion], [[UIScreen mainScreen] scale]];
        if (userAgent) {
            if (![userAgent canBeConvertedToEncoding:NSASCIIStringEncoding]) {
                NSMutableString *mutableUserAgent = [userAgent mutableCopy];
                if (CFStringTransform((__bridge CFMutableStringRef)(mutableUserAgent), NULL, (__bridge CFStringRef)@"Any-Latin; Latin-ASCII; [:^ASCII:] Remove", false)) {
                    userAgent = mutableUserAgent;
                }
            }
            [self setValue:userAgent forHTTPHeaderField:@"User-Agent"];
        }
        
    }
    return self;
}

- (NSDictionary<NSString *,NSString *> *)HTTPRequestHeaders
{
    __block NSDictionary *headers = nil;
    dispatch_sync(self.requestHeaderModificationQueue, ^{
        headers = [self.mutableHTTPRequestHeaders copy];
    });
    return headers;
}

- (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field
{
    dispatch_sync(self.requestHeaderModificationQueue, ^{
        [self.mutableHTTPRequestHeaders setValue:value forKey:field];
    });
}

- (NSString *)valueForHTTPHeaderField:(NSString *)field {
    __block NSString *value;
    dispatch_sync(self.requestHeaderModificationQueue, ^{
        value = [self.mutableHTTPRequestHeaders valueForKey:field];
    });
    return value;
}

- (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username
                                       password:(NSString *)password
{
    NSData *basicAuthCredentials = [[NSString stringWithFormat:@"%@:%@", username, password] dataUsingEncoding:NSUTF8StringEncoding];
    NSString *base64AuthCredentials = [basicAuthCredentials base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)0];
    [self setValue:[NSString stringWithFormat:@"Basic %@", base64AuthCredentials] forHTTPHeaderField:@"Authorization"];
}

- (void)clearAuthorizationHeader {
    dispatch_sync(self.requestHeaderModificationQueue, ^{
        [self.mutableHTTPRequestHeaders removeObjectForKey:@"Authorization"];
    });
}

- (nullable NSMutableURLRequest *)requestWithMethod:(NSString *)method
                                          URLString:(NSString *)URLString
                                         parameters:(nullable NSDictionary *)parameters
                                              error:(NSError *__autoreleasing * _Nullable)error
{
    if (!method.length || !URLString.length) {
        NSAssert(NO, @"requestWithMethod: method or URLString is empty");
    }
    
    NSURL *url = [NSURL URLWithString:URLString];
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
    request.HTTPMethod = method;
    
    request = [[self requestBySerializingRequest:request params:parameters error:error] mutableCopy];
    return request;
}

- (nullable NSURLRequest *)requestBySerializingRequest:(NSURLRequest *_Nullable)request
                                                params:(nullable NSDictionary *)params
                                                 error:(NSError *__autoreleasing * _Nullable)error
{
    NSMutableURLRequest *mutableRequest = [request mutableCopy];

    [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
        if (![request valueForHTTPHeaderField:field]) {
            [mutableRequest setValue:value forHTTPHeaderField:field];
        }
    }];
    
    //拼接参数
    NSString *query = AFQueryStringFromParameters(params);
    
    NSSet *parametersInURI = [NSSet setWithObjects:@"GET", @"HEAD", @"DELETE", nil];
    if ([parametersInURI containsObject:[request.HTTPMethod uppercaseString]]) {
        //拼接在url后面
        if (query && query.length > 0) {
            NSString *urlStr = [[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query];
            mutableRequest.URL = [NSURL URLWithString:urlStr];
        }
    }
    else {
        //设置在请求体中
        if (!query) {
            query = @"";
        }
        if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
            [mutableRequest setValue:@"application/x-www-form-urlencoded"
                  forHTTPHeaderField:@"Content-Type"];
        }
        mutableRequest.HTTPBody = [query dataUsingEncoding:self.stringEncoding];
    }
    return mutableRequest;
}

/*
 boundary是分割线
 
 [request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary]
 forHTTPHeaderField:@"Content-Type"];
 文件拼接格式：
 
 --{boundary}
 Content-Disposition: form-data; name="{name}"; filename="{filename}"
 Content-Type: {mimeType}

 {data}
 --{boundary}--
 
 
 示例：
 --Boundary+12345678ABCDEF
 Content-Disposition: form-data; name="fieldName"; filename="filename.jpg"
 Content-Type: image/jpeg

 (文件数据)
 --Boundary+12345678ABCDEF--
 */
- (nullable NSMutableURLRequest *)uploadWithUrlString:(NSString *)URLString
                                               params:(nullable NSDictionary<NSString *, NSString *> *)params
                                                 name:(NSString *)name
                                             fileName:(NSString *)fileName
                                                 data:(NSData *)data
                                             mimeType:(NSString *)mimeType
                                                error:(NSError *__autoreleasing * _Nullable)error
{
    if (!data || mimeType.length == 0 || fileName.length == 0 || name.length == 0) {
        if (error) {
            *error = [NSError errorWithDomain:@"UploadErrorDomain" code:1001 userInfo:@{NSLocalizedDescriptionKey: @"缺少必要的上传参数"}];
        }
        return nil;
    }
    NSMutableURLRequest *request = [self requestWithMethod:@"POST"
                                                 URLString:URLString
                                                parameters:nil
                                                     error:error];
    
    //请求头与分隔符
    NSString *boundary = AFCreateMultipartFormBoundary();
    [request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary] forHTTPHeaderField:@"Content-Type"];
    
    NSMutableData *bodyData = [NSMutableData data];
    // 添加普通参数：小文件直接将普调参数按格式转成data，拼接即可
    if (params) {
        for (NSString *key in params) {
            [bodyData appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:self.stringEncoding]];
            [bodyData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key] dataUsingEncoding:self.stringEncoding]];
            [bodyData appendData:[[NSString stringWithFormat:@"%@\r\n", params[key]] dataUsingEncoding:self.stringEncoding]];
        }
    }
    
    //按multipart/form-data格式拼接文件数据
    [bodyData appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:self.stringEncoding]];
    [bodyData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", name, fileName] dataUsingEncoding:self.stringEncoding]];
    [bodyData appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n\r\n", mimeType] dataUsingEncoding:self.stringEncoding]];
    
    //拼接数据
    [bodyData appendData:data];
    
    //拼接结束标记
    [bodyData appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", boundary] dataUsingEncoding:self.stringEncoding]];
    
    // 设置 Content-Length
    [request setValue:[NSString stringWithFormat:@"%lu", (unsigned long)[bodyData length]] forHTTPHeaderField:@"Content-Length"];
    
    // 小文件使用 HTTPBody 而不是 HTTPBodyStream 更可靠
    request.HTTPBody = bodyData;
    return request;
}

/*
 大文件为了优化内存，使用stream来实现：
 每个参数都是一个InputStream，参考AFN需要用复合stream来实现
 */
- (nullable NSMutableURLRequest *)uploadWithUrlString:(NSString *)URLString
                                               params:(nullable NSDictionary<NSString *, NSString *> *)params
                                                 name:(NSString *)name
                                             fileName:(NSString *)fileName
                                              fileUrl:(NSURL *)fileUrl
                                             mimeType:(NSString *)mimeType
                                                error:(NSError *__autoreleasing * _Nullable)error
{
    if (!fileUrl || mimeType.length == 0 || fileName.length == 0 || name.length == 0) {
        if (error) {
            *error = [NSError errorWithDomain:@"UploadErrorDomain" code:1001 userInfo:@{NSLocalizedDescriptionKey: @"缺少必要的上传参数"}];
        }
        return nil;
    }
    
    // 检查文件是否存在
    if (![fileUrl checkResourceIsReachableAndReturnError:error]) {
        return nil;
    }
    
    
    
    //分隔符
    NSString *boundary = AFCreateMultipartFormBoundary();
    
    ZYMultipartFormDataStream *bodyStream = [[ZYMultipartFormDataStream alloc]
                                             initWithBoundary:boundary
                                             params:params
                                             name:name
                                             fileName:fileName
                                             fileURL:fileUrl
                                             mimeType:mimeType
                                             stringEncoding:self.stringEncoding];
    
    
    NSMutableURLRequest *request = [self requestWithMethod:@"POST"
                                                 URLString:URLString
                                                parameters:nil
                                                     error:error];
    
    if (!request) {
        return nil;
    }
    
    // 设置请求头
    [request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary] forHTTPHeaderField:@"Content-Type"];
    [request setValue:[NSString stringWithFormat:@"%llu", bodyStream.contentLength] forHTTPHeaderField:@"Content-Length"];
    
    request.HTTPBodyStream = bodyStream;
    return request;
}

@end

